package com.example.user.service.auth.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.framework.common.exception.ServiceException;
import com.example.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.example.framework.common.util.EncryptUtils;
import com.example.framework.security.core.LoginUser;
import com.example.framework.security.core.dto.JwtTokenRespDto;
import com.example.framework.security.core.util.JwtTokenUtils;
import com.example.user.constants.ErrorCodeConstants;
import com.example.user.constants.SourceTypeEnum;
import com.example.user.controller.auth.vo.PasswordUpdateVO;
import com.example.user.controller.auth.vo.RegisterReqVO;
import com.example.user.dal.dataobject.user.UserDO;
import com.example.user.service.auth.AuthService;
import com.example.user.service.code.CodeService;
import com.example.user.service.code.CodeServiceFactory;
import com.example.user.service.user.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Resource
    private UserService userService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private JwtTokenUtils jwtTokenUtils;

    @Value("${encrypt.aes.secret}")
    private String aesSecret;

    @Resource
    private CodeServiceFactory codeServiceFactory;

    @Override
    public void register(RegisterReqVO reqDto) {
        String registerId = reqDto.getId();
        String encryptedRegisterId = EncryptUtils.AesEncrypt(registerId, aesSecret);
        Integer source = reqDto.getSource();

        // 1. 校验验证码
        verifyCode(source, registerId, reqDto.getCode());

        // 2. 校验用户是否存在
        if (userService.checkUserExists(reqDto)) {
            throw ServiceException.of(ErrorCodeConstants.USER_EXISTS);
        }

        // 3. 保存用户
        UserDO.UserDOBuilder userDOBuilder = UserDO.builder()
                // 不再通过用户名注册，改为通过邮箱注册，防止恶意刷注册
                .userId(IdUtil.simpleUUID())
                .name(reqDto.getName())
                .avatar(reqDto.getAvatar())
                .password(passwordEncoder.encode(reqDto.getPassword()))
                .source(reqDto.getRegisterSource())
                .registerCountryId(reqDto.getArea())
                .countryId(reqDto.getArea())
                .subscribeEmail(reqDto.getSubscribeEmail());
        if (source == SourceTypeEnum.SMS.getType()) {
            userDOBuilder.mobile(encryptedRegisterId);
        } else if (source == SourceTypeEnum.EMAIL.getType()) {
            userDOBuilder.email(encryptedRegisterId);
        } else {
            throw ServiceException.of(ErrorCodeConstants.BAD_REQUEST);
        }
        boolean save;
        try {
            save = userService.save(userDOBuilder.build());
        } catch (DuplicateKeyException e) {
            // 考虑并发情况
            throw ServiceException.of(ErrorCodeConstants.USER_EXISTS);
        }
        if (!save) {
            throw ServiceException.of(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public JwtTokenRespDto refreshToken(String refreshToken) {
        jwtTokenUtils.checkRefreshToken(refreshToken);
        LoginUser loginUser = jwtTokenUtils.getUserInfoFromCacheByToken(refreshToken);
        return jwtTokenUtils.generateTokenAndRefreshToken(loginUser);
    }

    @Override
    public void resetPassword(PasswordUpdateVO passwordUpdateVO) {
        String loginId = passwordUpdateVO.getId();
        Integer source = passwordUpdateVO.getSource();
        // 先校验验证码
        verifyCode(source, loginId, passwordUpdateVO.getCode());
        // 通过用户id/手机号/邮箱登录
        LambdaQueryWrapper<UserDO> queryWrapper = UserService.buildUserQueryWrapper(passwordUpdateVO, aesSecret)
                .select(UserDO::getId, UserDO::getUserId, UserDO::getPassword);
        UserDO userDO = userService.getOne(queryWrapper);
        if (Objects.isNull(userDO)) {
            throw ServiceException.of(GlobalErrorCodeConstants.USER_NOT_FOUND);
        }
        userDO.setPassword(passwordEncoder.encode(passwordUpdateVO.getNewPassword()));
        userService.updateById(userDO);

        // 清除缓存登录态，让其他端登录的用户强制退出
        jwtTokenUtils.removeToken(userDO.getUserId());
    }

    private void verifyCode(int source, String loginId, String code) {
        CodeService codeService = codeServiceFactory.get(SourceTypeEnum.get(source));
        codeService.verifyCode(loginId, code, true);
    }

}
